#pragma once

#include <Arduino.h>
#include <SPI.h>
#include <assert.h>

// SPI settings
#define SLAVE_SELECT_PIN 38
#define AD7193_RDY_STATE 37 // pin to watch for data ready state
#define SPI_CLOCK_SPEED SPI_CLOCK_DIV16

// Macros for setting mode bits
// #define AD7193_MODE_SEL(x) (((x) & 0x7) << 21) /*!< Operation Mode Select. */
// Mode Register Bit Designations (AD7193_REG_MODE)
#define AD7193_MODE_SEL(x) (((x) & 0x7) << 21)    // Operation Mode Select.
#define AD7193_MODE_DAT_STA (1 << 20)             // Status Register transmission.
#define AD7193_MODE_CLKSRC(x) (((x) & 0x3) << 18) // Clock Source Select.
#define AD7193_MODE_AVG(x) (((x) & 0x3) << 16)    // Fast settling filter.
#define AD7193_MODE_SINC3 (1 << 15)               // SINC3 Filter Select.
#define AD7193_MODE_SINC4 (0 << 15)               // SINC4 Filter Select.
#define AD7193_MODE_ENPAR (1 << 13)               // Parity Enable.
#define AD7193_MODE_CLKDIV (1 << 12)              // Clock divide by 2 (AD7190/2 only).
#define AD7193_MODE_SCYCLE (1 << 11)              // Single cycle conversion (zero latency).
#define AD7193_MODE_REJ60 (1 << 10)               // 50/60Hz notch filter.
#define AD7193_MODE_NO_REJ60 (0 << 10)            // No 50/60Hz notch filter.
#define AD7193_MODE_RATE(x) ((x) & 0x3FF)         // Filter Update Rate Select.

// Mode Register: AD7193_MODE_SEL(x) options
#define AD7193_MODE_CONT 0         // Continuous Conversion Mode.
#define AD7193_MODE_SINGLE 1       // Single Conversion Mode.
#define AD7193_MODE_IDLE 2         // Idle Mode.
#define AD7193_MODE_PWRDN 3        // Power-Down Mode.
#define AD7193_MODE_CAL_INT_ZERO 4 // Internal Zero-Scale Calibration.
#define AD7193_MODE_CAL_INT_FULL 5 // Internal Full-Scale Calibration.
#define AD7193_MODE_CAL_SYS_ZERO 6 // System Zero-Scale Calibration.
#define AD7193_MODE_CAL_SYS_FULL 7 // System Full-Scale Calibration.

/* AD7193 Register Map */
#define AD7193_REG_COMM 0      // Communications Register (WO, 8-bit)
#define AD7193_REG_STAT 0      // Status Register         (RO, 8-bit)
#define AD7193_REG_MODE 1      // Mode Register           (RW, 24-bit
#define AD7193_REG_CONF 2      // Configuration Register  (RW, 24-bit)
#define AD7193_REG_DATA 3      // Data Register           (RO, 24/32-bit)
#define AD7193_REG_ID 4        // ID Register             (RO, 8-bit)
#define AD7193_REG_GPOCON 5    // GPOCON Register         (RW, 8-bit)
#define AD7193_REG_OFFSET 6    // Offset Register         (RW, 24-bit
#define AD7193_REG_FULLSCALE 7 // Full-Scale Register     (RW, 24-bit)

/* Communications Register Bit Designations (AD7193_REG_COMM) */
#define AD7193_COMM_WEN (1 << 7)               // Write Enable.
#define AD7193_COMM_WRITE (0 << 6)             // Write Operation.
#define AD7193_COMM_READ (1 << 6)              // Read Operation.
#define AD7193_COMM_ADDR(x) (((x) & 0x7) << 3) // Register Address.
#define AD7193_COMM_CREAD (1 << 2)             // Continuous Read of Data Register.

/**
 * @brief This class manages the AD7193 Analog-to-Digital Converter.
 *
 * @details It provides functions for configuration, calibration, and data retrieval.
 */
class AD7193
{
public:
  struct AdcPack
  {
    uint32_t status : 8;
    uint32_t data : 24;
  };

  AD7193();

  // Initialize the AD7193
  bool begin(void);

  // Reset the AD7193
  void reset(void);

  // Calibrate the AD7193
  // void Calibrate(uint8_t mode);xiugai
  void Calibrate(void);

  // Calibrate the AD7193 Full scale
  void CalibrateFullScale(void);

  // Wait for the ADC to become ready
  void WaitForADC(void);

  // Set PGA gain
  void SetPGAGain(int gain);

  // Set the selected channel
  // void SetChannel(uint16_t channel);
  void SetChannel(int channel);
  // Set the Mode
  void setSinc(uint8_t mode);

  // Set Avariage
  void SetAverage(uint8_t average);

  void SetChop(uint8_t chop);

  // Append status value to data
  void AppendStatusValuetoData(void);

  // Read ADC data
  // AdcPack ReadADCData(void);

  // Read ADC channel
  // void ReadADCChannel(void);

  // Set the averaging filter rate
  void SetFilter(int filterRate);

  // Initiate continuous conversion
  void IntitiateContinuousConversion(void);

  // Set register value
  void SetRegisterValue(uint8_t registerAddress, uint32_t registerValue, uint8_t bytesNumber, uint8_t modifyCS);

  // Get register value
  uint32_t GetRegisterValue(uint8_t registerAddress, uint8_t bytesNumber, uint8_t modifyCS);

  void SetPsuedoDifferentialInputs(void);
  void IntitiateSingleConversion(void);
  float DataToVoltage(long rawData);
  unsigned long ReadADCChannel(int channel);
  unsigned long ReadADCData(void);
  void ReadRegisterMap(void);
};